﻿using Inaction.Resource.Properties;
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Inaction.Util
{
    public static class Reflect<T>
    {
        public static System.Reflection.MethodInfo GetMethod(Expression<Action<T>> method)
        {
            return GetMethodInfo(method);
        }

        public static System.Reflection.MethodInfo GetMethod<T1>(Expression<Action<T, T1>> method)
        {
            return GetMethodInfo(method);
        }

        public static System.Reflection.MethodInfo GetMethod<T1, T2>(Expression<Action<T, T1, T2>> method)
        {
            return GetMethodInfo(method);
        }

        public static System.Reflection.MethodInfo GetMethod<T1, T2, T3>(Expression<Action<T, T1, T2, T3>> method)
        {
            return GetMethodInfo(method);
        }
        private static System.Reflection.MethodInfo GetMethodInfo(Expression method)
        {
            if (method == null) throw new ArgumentNullException("method");

            var lambda = method as LambdaExpression;
            if (lambda == null)
            {
                throw new ArgumentException(Resources.NotIsLambdaExpression, "method");
            }
            if (lambda.Body.NodeType != ExpressionType.Call)
            {
                throw new ArgumentException(Resources.NotIsMethodCall, "method");
            }
            return ((MethodCallExpression)lambda.Body).Method;
        }

        public static PropertyInfo GetProperty(Expression<Func<T, object>> property)
        {
            var info = GetMemberInfo(property) as PropertyInfo;
            if (info == null) throw new ArgumentException(Resources.MemberIsNotProperty);
            return info;
        }

        public static PropertyInfo GetProperty<P>(Expression<Func<T, P>> property)
        {
            var info = GetMemberInfo(property) as PropertyInfo;
            if (info == null) throw new ArgumentException(Resources.MemberIsNotProperty);

            return info;
        }

        public static FieldInfo GetField<P>(Expression<Func<T, P>> field)
        {
            var info = GetMemberInfo(field) as FieldInfo;
            if (info == null) throw new ArgumentException("Member is not a field");

            return info;
        }

        private static MemberInfo GetMemberInfo(Expression member)
        {
            if (member == null) throw new ArgumentNullException("member");

            var lambda = member as LambdaExpression;
            if (lambda == null) throw new ArgumentException("Not a lambda expression", "member");

            MemberExpression memberExpr = null;

            if (lambda.Body.NodeType == ExpressionType.Convert)
            {
                memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
            }
            else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
            {
                memberExpr = lambda.Body as MemberExpression;
            }

            if (memberExpr == null) throw new ArgumentException("Not a member access", "member");

            return memberExpr.Member;
        }
    }
}
